package editor.manager;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JViewport;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import editor.Context;
import editor.JavaEditorPane;
import editor.Mode;
import editor.mouse.TabMouseAdapter;
import editor.project.ProjectFile;

/**
 * TextArea
 * 
 * @author Mark Dessain
 */
@SuppressWarnings("serial")
public final class TextAreaManager extends JTabbedPane implements ChangeListener, ActionListener, Manager
{
    
    private ArrayList<ProjectFile> fileList = new ArrayList<ProjectFile>();
    private HashMap<JButton, JScrollPane> closeButtonList = new HashMap<JButton, JScrollPane>();
    private HashMap<JavaEditorPane, JLabel> labelList = new HashMap<JavaEditorPane, JLabel>();
    
    private Object source;
    
    private ImageIcon iconCancel = new ImageIcon("graphics/icons/cancel.png");
	private ImageIcon iconCancelGray = new ImageIcon("graphics/icons/cancel_gray.png");
   	
    /**
     * 
     */
    public TextAreaManager()
	{
        setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
        addChangeListener(this);  
        
        addMouseListener(new TabMouseAdapter());	
        
        loadFiles();
    }
    
	/**
	 * 
	 */
	public void loadFiles()
	{	
		ArrayList<ProjectFile> files = Context.getEditorSettings().getFiles();
		for(ProjectFile file: files)
		{
			add(file, true);
		}
		
		if(files.isEmpty())
		{
			Context.getSideBarManager().setEnableTestClass(false);
		}
	}


    /**
     * @param file to be added
     * @param loader True if its to be added to the file list, False otherwise
     */
    public void add(ProjectFile file, boolean loader)
    {
    	if(!fileList.contains(file))
    	{
			fileList.add(file);
			try 
			{		
				String title = file.getName();
				String content = loadTab(file.getFileLocation());
				
				JavaEditorPane codeEditor = new JavaEditorPane();
		        JScrollPane scrPane = new JScrollPane(codeEditor);
		        
				add(scrPane);
				this.setSelectedComponent(scrPane);
		        addCloseButton(title, getSelectedIndex(), scrPane);
		        
				codeEditor.setText(content);
				
				if(Context.getMode() == Mode.QUESTION)
				{
					codeEditor.setEditable(false);
				}
		        
		        if(!loader)
		        {
		        	// Add to the settings XML
		        	Context.getEditorSettings().addFile(file);
		        }
		        
		        this.setRemoveChanged(codeEditor); 
			} 
			catch (IOException e) 
			{
				JOptionPane.showMessageDialog(Context.getJwalkEditor(), "The file " + file.getFileLocation() + " could not be loaded.", "Unable to load file", JOptionPane.ERROR_MESSAGE);
				e.printStackTrace();
			}
    	}
    	else
    	{
    		// TODO check if the file has changed, warn the user and ask if they want to update the current opened file
    		setSelectedIndex(fileList.indexOf(file));
    	}
    	
    	setTestClassButtons();
    }

    /* (non-Javadoc)
     * @see javax.swing.JTabbedPane#remove(int)
     */
    @Override
    public void remove(int index)
    {
    	if(index > -1)
    	{
    		Context.getEditorSettings().removeFile(fileList.get(index));
    		fileList.remove(index);
    		super.remove(index);
    	}
    	setTestClassButtons();
    }
    
    /**
     * @return True if the editor is save to close and the user has saved all the files which they want to
     */
    public boolean closeSave()
    {
    	Component[] c = this.getComponents();
    	
    	for(int i = this.getComponentCount() - 1; i >= 0 ; i--)
		{			
    		if(c[i] instanceof JScrollPane)
    		{
    			int answer = 0;
    			
    			JavaEditorPane editorPane = ((JavaEditorPane)((JViewport)((JScrollPane)c[i]).getComponent(0)).getComponent(0));
        		if(editorPane.getChanged())
        		{
        			String message = "Do you want to save " + labelList.get(editorPane).getText() + "?";
        		    answer = JOptionPane.showConfirmDialog(Context.getJwalkEditor(), message);
        		    if (answer == JOptionPane.YES_OPTION) {
        		    	save((JScrollPane)c[i]);
        		    }
        		}
        		
        		if(answer == JOptionPane.CANCEL_OPTION)
        		{
        			return false;
        		}
    		}
		}    
    	
    	return true;
    }

    /* (non-Javadoc)
     * @see javax.swing.JTabbedPane#remove(java.awt.Component)
     */
    public void remove(Component component)
    { 
    	int answer = 0;
 
    	// Only attempt to remove the component if it exists
    	if(component != null)
    	{
    		JavaEditorPane editorPane = ((JavaEditorPane)((JViewport)((JScrollPane)component).getComponent(0)).getComponent(0));
    		if(editorPane.getChanged())
    		{
    			String message = "Do you want to save " + labelList.get(editorPane).getText() + "?";
    		    answer = JOptionPane.showConfirmDialog(Context.getJwalkEditor(), message);
    		    if (answer == JOptionPane.YES_OPTION) {
    		    	save((JScrollPane)component);
    		    }
    		}
    		
    		if(answer != JOptionPane.CANCEL_OPTION)
    		{
	    		closeButtonList.remove(source);
	    		Context.getEditorSettings().removeFile(fileList.get(indexOfComponent(component)));
	    		fileList.remove(indexOfComponent(component));
	    		setTestClassButtons();
	
		    	super.remove(component);
    		}
    	}
    	
    }
    
    /* (non-Javadoc)
     * @see javax.swing.JTabbedPane#removeAll()
     */
    @Override
    public void removeAll()
    {

    	Component[] c = this.getComponents();
    	
    	for(int i = this.getComponentCount() - 1; i >= 0 ; i--)
		{			
    		if(c[i] instanceof JScrollPane)
    		{
    			System.out.println(i);
    			this.remove(c[i]);
    		}
		}
    }
    
    
    /**
     * @return the currently selected JavaEditorPane
     */
    public JavaEditorPane getSelectedEditor()
    {
    	return getEditorAt(this.getSelectedIndex());
    }
    
   	/**
   	 * @param index of editor
   	 * @return the JavaEditorPane at the index
   	 */
   	public JavaEditorPane getEditorAt(int index)
    {
    	Component component = this.getComponentAt(index);
    	return getEditorAt(component);
    }
   	
   	/**
   	 * @param component of tab
   	 * @return the JavaEditorPane at the tab
   	 */
   	public JavaEditorPane getEditorAt(Component component)
    {
    	JavaEditorPane editorPane = (JavaEditorPane)((JViewport)((JScrollPane)component).getComponent(0)).getComponent(0);
    	
    	return editorPane;
    }
   	
   	/**
   	 * @return returns all the editors
   	 */
   	public ArrayList<JavaEditorPane> getAllEditors()
   	{
   		ArrayList<JavaEditorPane> editorList = new ArrayList<JavaEditorPane>();
   		
   		for(int i = 0; i < getTabCount(); i++)
   		{
   			editorList.add(getEditorAt(i));
   		}
   		
   		return editorList;
   	}
    
    /**
     * @param editorPane to select
     * @param line number to highlight
     */
    public void selectLine(JavaEditorPane editorPane, int line)
    {
    	editorPane.selectLine(line);
    }
    
    /**
     * @param editorPane to select
     * @param index on the line which needs highlighting
     */
    public void selectLineAtIndex(JavaEditorPane editorPane, int index)
    {
    	int line = editorPane.getLine(index);
    	editorPane.selectLine(line + 1);
    }
    
    /**
     * @param index of file
     * @return file at the index
     */
    public ProjectFile getFileAt(int index)
    {
    	return fileList.get(index);
    }
    
    /**
     * @param file 
     * @return index of the file
     */
    public int getIndexOfFile(ProjectFile file)
    {
    	return fileList.indexOf(file);
    }
     
    /**
     * @param component to save
     */
    public void save(Component component)
    {
    	if(component != null)
    	{
    		int index = indexOfComponent(component);
    		ProjectFile file = fileList.get(index);
    	 
    		saveAs(component, file);
    	}
    }
    
    /**
     * @param component to save
     * @param fileName new file to saveas
     */
    public void saveAs(Component component, ProjectFile fileName)
    {
    	if(component != null)
    	{
	    	JavaEditorPane editorPane = getEditorAt(component);
	    	
	        try
	        {
	        	// Create file 
	            FileWriter fstream = new FileWriter(fileName.getFileLocation());
	            BufferedWriter out = new BufferedWriter(fstream);
	            out.write(editorPane.getText());
	            
	            out.close();
	            
	            setRemoveChanged(editorPane);
	        }
	        catch (Exception e)
	        {
	              System.err.println("Error: " + e.getMessage());
	        }   	
    	}
    }
    
    /**
     * Save all the files open which need saving
     */
    public void saveAll()
    {
    	for(int i = 0; i < this.getTabCount(); i++)
    	{
    		save(this.getComponentAt(i));
    	}
    }
    
    /**
     * @param filename to load
     * @return the file content string
     * @throws IOException
     */
    private String loadTab(String filename) throws IOException
    {		
        BufferedReader br = null;
        StringBuffer sb = new StringBuffer();
        try{
            br = new BufferedReader(new FileReader(filename));
            String nextLine = "";
            while ((nextLine = br.readLine()) != null) {
                sb.append(nextLine);
                sb.append("\n");
            }
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } finally {
            try {
                br.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        
        return sb.toString();
    }

    /* (non-Javadoc)
     * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
     */
    public void stateChanged(ChangeEvent e) 
    {
    	
    	Set<JButton> closeButtons = this.closeButtonList.keySet();

        // Change all the buttons which are not selected to have a grayed out icon
    	for(JButton button: closeButtons)
    	{
    		
    		if(closeButtonList.get(button) == this.getSelectedComponent())
    		{
    			button.setIcon(iconCancel);
    		}
    		else
    		{
    			button.setIcon(iconCancelGray);
    		}
    	}
    }
    
    /**
     * @param title of tab
     * @param index of tab
     * @param editorScroll of tab
     */
    public void addCloseButton(String title, int index, JScrollPane editorScroll)
    {
        JLabel label = new JLabel(title);
        JButton close = new JButton();   
        JPanel tabPane = new JPanel();
        tabPane.setOpaque(false);
        tabPane.add(label);
        tabPane.add(close);
        
        close.setPreferredSize(new Dimension(12, 12));

    	ImageIcon fileJava = new ImageIcon("graphics/icons/cancel.png");
        close.setIcon(fileJava);
        
        labelList.put((JavaEditorPane)((JViewport)(editorScroll).getComponent(0)).getComponent(0), label);
        closeButtonList.put(close, editorScroll);
        
        setTabComponentAt(index, tabPane);  
        
        close.addActionListener(this);	
        
    }

    /**
     * @param editorPane editor which ha been changed
     */
    public void setSelectedChanged(JavaEditorPane editorPane)
    {
    	
    	editorPane.setChanged(true);
    	JLabel label = labelList.get(editorPane);
    	
    	System.out.println(label + " " + editorPane);
    	label.setText(label.getText() + "*");
    }
    
    /**
     * @param editorPane editor where changes have been removed
     */
    public void setRemoveChanged(JavaEditorPane editorPane)
    {
    	System.out.println(editorPane);
    	if(editorPane.getChanged())
    	{
    		JLabel label = labelList.get(editorPane);
    		
    		System.out.println(label);
    		
        	label.setText(label.getText().substring(0, label.getText().length() - 1));
    	}
    	
    	editorPane.resetUndoManager();
    }
    

	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	@Override
	public void actionPerformed(ActionEvent e) 
	{
		// Select the source
		source = e.getSource();
		
		JScrollPane editorPane = closeButtonList.get(source);
		if(editorPane != null)
		{	
    		this.remove(editorPane);
		}
	}
	
	/**
	 * set test class button 
	 */
	public void setTestClassButtons()
	{
		if(getSelectedComponent() == null)
		{
			Context.getSideBarManager().setEnableTestClass(false);
		}
		else
		{
			Context.getSideBarManager().setEnableTestClass(true);
		}
	}

	/* (non-Javadoc)
	 * @see editor.manager.Manager#switchMode(editor.Mode)
	 */
	@Override 
	public void switchMode(Mode mode)
	{
		if(mode == Mode.CODING)
		{
			codingMode();
		}
		else if(mode == Mode.QUESTION)
		{
			questionMode();	
		}
		else if(mode == Mode.TESTING)
		{
			testingMode();
		}
	}

	/* (non-Javadoc)
	 * @see editor.manager.Manager#codingMode()
	 */
	@Override
	public void codingMode() 
	{
		for(JavaEditorPane editor: getAllEditors())
		{
			editor.setEditable(true);
		}
	}

	/* (non-Javadoc)
	 * @see editor.manager.Manager#questionMode()
	 */
	@Override
	public void questionMode()
	{
		for(JavaEditorPane editor: getAllEditors())
		{
			editor.setEditable(false);
			editor.setEnabled(true);
		}		
	}

	/* (non-Javadoc)
	 * @see editor.manager.Manager#testingMode()
	 */
	@Override
	public void testingMode() 
	{
		for(JavaEditorPane editor: getAllEditors())
		{
			editor.setEditable(true);
		}		
	}
}